package com.ld.shieldsb.annotation.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.ld.shieldsb.annotation.field.db.ColumnName;
import com.ld.shieldsb.annotation.field.db.Unique;
import com.ld.shieldsb.annotation.model.ModelAnno.Model;
import com.ld.shieldsb.common.core.collections.ListUtils;
import com.ld.shieldsb.common.core.model.Description;
import com.ld.shieldsb.common.core.reflect.ModelUtil;
import com.ld.shieldsb.common.core.util.StringUtils;

public class AnnotationUtil extends ModelUtil {
    private static final Map<String, List<Field>> CLAZZ_ANNO_FIELDS = new HashMap<>(); // 缓存 类中属性的注解对应关系
    private static final Map<String, List<List<Description>>> CLAZZ_ANNO_UNIQUE_FIELDS = new HashMap<>(); // 缓存 类中不重复属性的注解对应关系
    private static final Map<String, Description> CLAZZ_DESP_FIELDS = new HashMap<>(); // 缓存 类中属性的描述对象

    /**
     * 返回当前对象（不包含父类）field的map键值对，键为属性名或者注解名转换为小写，值为Field对象
     * 
     * @Title getModelFieldMap
     * @author 吕凯
     * @date 2019年6月27日 上午8:18:38
     * @param modelType
     * @param fieldNameFirst
     *            属性名优先，否则优先取注解名
     * @return Map<String,Field>
     */
    private static Map<String, Field> getThisModelFieldsMap(Class<?> modelType, boolean fieldNameFirst) {
        Map<String, Field> fieldMap = new LinkedHashMap<String, Field>();
        Field[] fields = modelType.getDeclaredFields();
        for (Field field : fields) {
            String name = field.getName();
            if (!fieldNameFirst) { // 为false时则注解名优先
                ColumnName columnName = field.getAnnotation(ColumnName.class);
                if (columnName != null) {// 注解名优先
                    name = columnName.value();
                }
            }
            fieldMap.put(name.toLowerCase(), field);
        }

        return fieldMap;
    }

    // 获取所有父类列表，直到Object类（不含Object类）
    private static List<Class<?>> getSuperTypeList(Class<?> type) {
        List<Class<?>> classList = new LinkedList<Class<?>>();
        classList.add(type);
        Class<?> tempType = type;
        while (!(tempType = tempType.getSuperclass()).equals(Object.class)) {
            classList.add(0, tempType);
        }
        return classList;
    }

    /**
     * 获取field的Map注释名优先
     * 
     * @param modelType
     * @return
     */
    public static Map<String, Field> getFieldMap(Class<?> modelType) {
        return getFieldMap(modelType, false);
    }

    /**
     * 获取field的Map
     * 
     * @Title getFieldMap
     * @author 吕凯
     * @date 2017年11月27日 下午2:08:26
     * @param modelType
     * @param fieldNameFirst
     *            field的名字优先，false为注解名优先
     * @return Map<String,Field>
     */
    public static Map<String, Field> getFieldMap(Class<?> modelType, boolean fieldNameFirst) {
        Map<String, Field> map = new LinkedHashMap<String, Field>();
        List<Class<?>> modelTypeList = getSuperTypeList(modelType); // 获取所有父类
        for (Class<?> type : modelTypeList) {
            map.putAll(getThisModelFieldsMap(type, fieldNameFirst));
        }
        return map;
    }

    /**
     * 获取model的展示名称
     * 
     * @Title getModelShowName
     * @author 吕凯
     * @date 2019年1月15日 下午12:34:53
     * @param modelBean
     * @return String
     */
    public static <T> String getModelShowName(T modelBean) {
        String objShowName = "";
        Model descriptionAnno = modelBean.getClass().getAnnotation(Model.class);
        if (descriptionAnno != null) {
            objShowName = descriptionAnno.name();
        }
        return objShowName;
    }

    /**
     * 
     * 获取model的展示名称
     * 
     * @Title getModelShowName
     * @author 吕凯
     * @date 2019年1月15日 下午12:39:19
     * @param cls
     * @return String
     */
    public static String getModelShowName(Class<?> cls) {
        String objShowName = "";
        Model descriptionAnno = cls.getAnnotation(Model.class);
        if (descriptionAnno != null) {
            objShowName = descriptionAnno.name();
        }
        return objShowName;
    }

    /**
     * 获取Field的展示名称
     * 
     * @Title getFiledShowName
     * @author 吕凯
     * @date 2019年1月16日 下午3:53:48
     * @param field
     * @return String
     */
    public static <T> String getFiledShowName(Field field) {
        return getFiledShowName(field, "");
    }

    /**
     * 获取属性的展示名称
     * 
     * @Title getFiledShowName
     * @author 吕凯
     * @date 2019年7月25日 下午2:15:58
     * @param field
     *            属性
     * @param defaultName
     *            默认名称
     * @return String
     */
    public static <T> String getFiledShowName(Field field, String defaultName) {
        String objShowName = defaultName;
        if (field != null) {
            objShowName = field.getName();
            com.ld.shieldsb.annotation.field.DealField.Field descriptionAnno = field
                    .getAnnotation(com.ld.shieldsb.annotation.field.DealField.Field.class);
            if (descriptionAnno != null) {
                objShowName = descriptionAnno.name();
            }
        }
        return objShowName;
    }

    /**
     * 获取类中有某个注解的属性
     * 
     * @Title getLinkFieldName
     * @author 吕凯
     * @date 2019年7月25日 下午1:56:32
     * @param modelType
     * @param annotationClass
     * @return List<Field>
     */
    public static <T> List<Field> getAnnoFields(Class<T> modelType, Class<? extends Annotation> annotationClass) {
        String key = modelType.getName() + "_" + annotationClass.getName();
        List<Field> fields = CLAZZ_ANNO_FIELDS.get(key);
        if (!CLAZZ_ANNO_FIELDS.containsKey(key)) {
            fields = new ArrayList<>();
            Map<String, Field> fieldMap = AnnotationUtil.getFieldMap(modelType); // 获取类的field
            for (String fildKey : fieldMap.keySet()) {
                Field field = fieldMap.get(fildKey);
                field.setAccessible(true);
                if (field.isAnnotationPresent(annotationClass)) {
                    fields.add(field);
                }
            }
            CLAZZ_ANNO_FIELDS.put(key, fields);
        }
        return fields;
    }

    /**
     * 根据属性名获取Field描述
     * 
     * @Title getFieldDescription
     * @author 吕凯
     * @date 2019年8月23日 上午8:06:24
     * @param modelType
     * @param fieldName
     * @return Description
     */
    public static <T> Description getFieldDescription(Class<T> modelType, String fieldName) {
        Field field = ModelUtil.getField(modelType, fieldName);
        return getFieldDescription(modelType, field);
    }

    public static <T> Description getFieldDescription(Class<T> modelType, Field field) {
        Description fieldDesc = null; // 先从缓存中取
        if (field != null) {
            String key = modelType.getName() + "_" + field.getName();
            fieldDesc = CLAZZ_DESP_FIELDS.get(key); // 先从缓存中取
            if (!CLAZZ_DESP_FIELDS.containsKey(key)) { // 没有则查询
                fieldDesc = new Description();
                String fildKey = field.getName();

                ColumnName columnName = field.getAnnotation(ColumnName.class);
                if (columnName != null) {
                    fildKey = columnName.value();
                }

                fieldDesc.setDbName(fildKey);
                fieldDesc.setName(field.getName());
                fieldDesc.setShowName(AnnotationUtil.getFiledShowName(field));
                CLAZZ_DESP_FIELDS.put(key, fieldDesc);
            }
        }
        return fieldDesc;
    }

    /**
     * 根据属性名获取Field描述
     * 
     * @author 吕凯
     * @date 2019年8月23日 上午8:10:05
     * @param model
     *            对象
     * @param fieldName
     *            属性名
     * @return Description
     */
    public static <T> Description getFieldDescription(T model, String fieldName) {
        return getFieldDescription(model.getClass(), fieldName);
    }

    /**
     * 获取不重复的字段名称
     * 
     * @Title getUniqueFieldName
     * @author 吕凯
     * @date 2019年1月16日 下午3:15:06
     * @param modelBean
     * @return List<List<String>>
     */
    public static <T> List<List<Description>> getUniqueFieldName(Class<T> modelType) { //
        String key = modelType.getName();
        List<List<Description>> uniqueField = new ArrayList<>();
        if (!CLAZZ_ANNO_UNIQUE_FIELDS.containsKey(key)) {
            List<Field> fields = getAnnoFields(modelType, Unique.class);
            if (ListUtils.isNotEmpty(fields)) {
                fields.forEach(field -> {
                    Unique uniqueAnno = field.getAnnotation(Unique.class);
                    List<Description> uniqueList = new ArrayList<>();
                    Description fieldDesc = new Description();
                    String fildKey = field.getName();

                    ColumnName columnName = field.getAnnotation(ColumnName.class);
                    if (columnName != null) {
                        fildKey = columnName.value();
                    }

                    fieldDesc.setDbName(fildKey);
                    fieldDesc.setName(field.getName());
                    fieldDesc.setShowName(AnnotationUtil.getFiledShowName(field));
                    uniqueList.add(fieldDesc);
                    String compsite = uniqueAnno.compsite(); // 多个字段联合用,分隔，注意需要写数据库名称
                    if (StringUtils.isNotBlank(compsite)) {
                        String[] compsiteArrs = StringUtils.getStrArr(compsite); // 获取多字段数组
                        for (int i = 0; i < compsiteArrs.length; i++) { // 每个字段都分别组成Description对象
                            String compFileName = compsiteArrs[i];
                            Field compField = ModelUtil.getField(modelType, compFileName);

                            String compDBName = compField.getName();

                            ColumnName compColumnName = compField.getAnnotation(ColumnName.class);
                            if (compColumnName != null) {
                                compDBName = compColumnName.value();
                            }

                            Description fieldCompsiteDesc = new Description();
                            fieldCompsiteDesc.setDbName(compDBName); // 需进一步处理
                            fieldCompsiteDesc.setName(compFileName);
                            fieldCompsiteDesc.setShowName(AnnotationUtil.getFiledShowName(compField, compFileName));
                            uniqueList.add(fieldCompsiteDesc);
                        }
                    }
                    uniqueField.add(uniqueList);
                });
            }

            CLAZZ_ANNO_UNIQUE_FIELDS.put(key, uniqueField);
        } else {
            uniqueField.addAll(CLAZZ_ANNO_UNIQUE_FIELDS.get(key));

        }
        return uniqueField;
    }

}
